/**
 * 获取参数配置
 * 
 * 监听端口
 */

/**
 * git钩子地址：http://127.0.0.1:8800/push
 * 
nginx 代理设置
location /xx/
{
    proxy_pass http://127.0.0.1:8801/;
}
 */

var _lib = {}
_lib.fs = require("fs");
_lib.path = require('path');
_lib.url = require('url');
_lib.http = require('http');
_lib.https = require('https');
_lib.child_process = require('child_process');

var _config = function(){
	var p = process.argv.length>=3?_lib.path.resolve(process.argv[2]):__dirname;
	if(p[p.length-1] != '/')p += '/';
	return {
		path:p,
		port:process.argv.length >= 4?process.argv[3]:8801,
	}
}();

var default_conf = `{
	"git":[],
	"bark_token":"yZnopuEE3FjgwwTe9GZpxQ",
	"username":"admin",
	"password":"123456"
}`

var post = function(url,data){
	return new Promise(ok=>{
		var option = _lib.url.parse(url);
		option.method = 'GET';
		if(data){
			var headers = {}
			var body = JSON.stringify(data)
			headers['Content-Type']		= 'application/json; charset=utf-8';
			headers['Content-Length']	= Buffer.byteLength(body)

			option.method = 'POST';
			option.headers = headers;
		}
		var request = _lib.https.request(option,res=>{
			let data = '';
			res.on('data',chunk=>{
				data += chunk.toString();
			})
			res.on('end',()=>{
				ok(data)
			})
		})
		if(body){
			request.write(body);
		}
		request.end();
	})
}

var Server = function(port){
	var url_list = {};
	const server = _lib.http.createServer(async function(req,res){
		var url = req.url;
		var pos = url.indexOf('?');
		var query = {},_query = '';
		if(pos !== -1){
			_query = url.substr(pos+1);
			url = url.substr(0,pos);
			var d = _query.split('&')
			for(var v of d){
				pos = v.indexOf('=');
				if(pos !== -1){
					query[v.substr(0,pos)] = v.substr(pos+1);
				}else{
					query[v] = '';
				}
			}
		}
		var post = null;
		if(req.method !== 'GET'){
			post = await new Promise(ok=>{
				var str = '';
				req.on('data',data=>str += data);
				req.on('end',()=>{
					try{
						if(str){
							var post = JSON.parse(str)
							ok(post)
						}
					}catch(e){
						ok(null)
					}
				})
			})
		}
		if(typeof url_list[url] === 'function'){
			var args = {
				get:query,
				post,
				req,
				res,
				echo:'',
			}
			var str = await url_list[url](args);
			if(str || args.echo){
				res.setHeader("Content-type","text/html;charset=utf8");
				res.end((typeof args.echo === 'string'?args.echo:JSON.stringify(args.echo))+(typeof str === 'string'?str:JSON.stringify(str)));
				return;
			}
		}
		// res.statusCode = 404;
		// res.end('404');
	})

	server.listen(port,function(){
		console.log('port:'+port)
	})

	return url_list;
};

(async function(){
	var conf = await F(_config.path+'config.json')
	conf = JSON.parse(conf||default_conf)

	var Bark = function(){
		return {
			async send(data){
				if(typeof conf.bark_token !== 'undefined'){
					post('https://api.day.app/push',{
						...data,
						device_key:conf.bark_token,
					});
				}
			}
		}
	};
	var bark = Bark();
	
	var server = Server(_config.port)

	var runjs = function(path,code){
		return new Promise(ok=>{
			var args = code.split(/\s+/)
			var exe = args.shift()

			var workerProcess = _lib.child_process.spawn(exe, args ,{cwd:path});

			var data = ''
			workerProcess.stdout.on('data', function (str) {
				data += str
			});
		
			workerProcess.on('close', function (code) {
				ok({code,data});
			});
		})
	}

	server['/'] = async (d)=>{
		if(!is_login(d))return;

		var str = await F(_config.path+'config.json')

		var log = [];
		for(let item of conf.git){
			log.push({name:item.name,logs:item.logs})
		}

		return `
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
	<title>git_hooks</title>
	<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.13/theme-chalk/index.css">
	<style>
		html,body{height:100%;}
		body{margin:0;}
		.app{padding:20px;box-sizing: border-box;max-width:800px;margin:auto;}
		.app .conf{flex:1;height:0;}
		.app .conf .el-textarea__inner{width:100%;height:100%;}
		.hh{height:500px;display:flex;flex-flow: column;overflow:auto;}
	</style>
</head>
<body>
	<div class="app" id="app">
		<el-tabs type="border-card">
			<el-tab-pane label="" v-for="(v,k) in list" :key="k" :label="v.name">
				<div class="hh">
					<div v-for="(v2,k2) in v.logs" style="border:1px solid #ccc">
						<div v-text="v2.sh"></div>
						<pre v-text="v2.log"></pre>
					</div>
				</div>
			</el-tab-pane>
			<el-tab-pane label="配置">
				<div class="hh">
					<el-input type="textarea" ref="conf" v-model="text" class="conf"></el-input>
					<div>
						<el-button style="margin-top:10px" type="primary" @click="submit()"><span v-text="'保存'"></span></el-button>
					</div>
				</div>
			</el-tab-pane>
			<el-tab-pane label="说明">
<pre>
{
	"git":[
		{
			"name":"项目名称",
			"path":"/www/wwwroot/xxx.com",			//git拉取地址
			"command":[
				"/www/wwwroot/xxx.com"			//执行npm run build的地址
			]
		}
	],
	"bark_token":"yZnopuEE3FjgwwTe9GZpxQ",
	"username":"admin",
	"password":"123456"
}
</pre>
			</el-tab-pane>
		</el-tabs>
	</div>
	<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.9/vue.min.js"></script>
	<script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.13/index.js"></script>
	<script>
		new Vue({
			el: '#app',
			data: function() {
				return {
					text:\`${str || default_conf}\`,
					list:${JSON.stringify(log)}
				}
			},
			methods:{
				submit(){
					if(!confirm('确认保存'))return;

					try{
						var d = JSON.parse(this.text)
						if(typeof d.git === 'undefined' ||
						 typeof d.bark_token === 'undefined' ||
						 typeof d.username === 'undefined' ||
						 typeof d.password === 'undefined'){
							alert('格式不正确')
							return;
						 }
					}catch(e){
						alert('格式不正确')
						return;
					}

					fetch('conf/set', {
						method: 'post',
						body: this.text,
						headers: {
							'Content-Type': 'application/json'
						}
					}).then(function(data) {
						alert('保存成功')
						location.reload()
					},function(){

					})
				}
			},
			mounted(){
				this.$refs.conf.$el.addEventListener('keydown', (e)=>{
					if(e.keyCode === 83 && e.ctrlKey){
						e.preventDefault()
						e.returnValue = false

						this.submit()
					}
				})
			}
		})
	</script>
</body>
</html>
		`;
	}

	var is_login = (d)=>{
		var auth = d.req.headers['authorization'];
		if(auth){
			var tmp = auth.split(' '); 
			var buf = Buffer.from(tmp[1], 'base64');                 
			var plain_auth = buf.toString();
			var creds = plain_auth.split(':');
			var username = creds[0];
			var password = creds[1];
			if(username === conf.username && password === conf.password){
				return true;
			}
		}
		d.res.statusCode = 401;
		d.res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
		d.res.end();
		return false;
	}

	//修改配置
	server['/conf/set'] = async (d)=>{
		if(!is_login(d))return;
		conf = d.post
		
		await F(_config.path+'config.json',JSON.stringify(conf,null,'\t'))
		return [0];
	}

	var togit = async (okfn,d,item,ref)=>{

		var res = await runjs(item.path,'git rev-parse --is-inside-work-tree')
		if(res.data.trim() === 'true'){
			//获取版本信息
			var git_version = (await runjs(item.path,'git remote -v')).data

			var clone_url = _lib.url.parse(d.post.repository.clone_url || d.post.repository.git_http_url)
			if(git_version.indexOf(clone_url.hostname+clone_url.pathname) !== -1){
				var start = time()
		
				//判断是否是当前分支提交
				let head = await runjs(item.path,'git rev-parse --abbrev-ref HEAD')
				console.log(clone_url.hostname+clone_url.pathname+':'+ref)
				if(ref == head.data.trim()){
					console.log('start')
					await runjs(item.path,'git pull')
					
					if(item.command && item.command.length)
					for(var path of item.command){
						item.logs = [];
						var log = await runjs(path,'npm run build')
						item.logs.push({
							path:item.path,
							sh:'npm run build',
							code:log.data,
							log:log.data,
						})
					}

					console.log('end')
					var end = time()
					bark.send({
						title:'代码发布通知:'+item.name,
						body:'执行时间:'+(end == start?'0秒':date('H时i分s秒',-(end - start)))+'\n'+'开始时间:'+date('Y-m-d H:i:s',start)
					})
				}
			}
		}

		okfn();
	}

	//git钩子通知
	server['/push'] = async (d)=>{
		if(!(d.post && d.post.repository && (d.post.repository.clone_url || d.post.repository.git_http_url)))return 'error';

		let ref = d.post.ref.split('/').pop()
		
		if(typeof conf.git !== 'undefined'){
			conf.git.forEach(async item=>{
				if(typeof item.is_run == 'undefined' || item.is_run === false){
					item.is_run = true;
					var f = ()=>{
						if(item.is_lis){
							item.is_lis = false
							togit(f,d,item,ref);
						}else{
							item.is_run = false;
						}
					}
					togit(f,d,item,ref);
				}else{
					item.is_lis = true;
				}
			})
		}

		return 'ok'
	}

})()

time = function(o){
	var t = o||new Date();
	return parseInt(t.getTime()/1000);
}

date = function(str){
	var t;
	switch(arguments.length){
		case 1:
			t = new Date();
		break;
		case 2:
			if(Object.prototype.toString.call(arguments[1]) === '[object Date]'){
				t = arguments[1];
			}else if(typeof arguments[1] === 'number' && arguments[1].toString().length > 10){
				t = new Date(arguments[1]);
			}else if(arguments[1] >= 0){
				t = new Date(parseInt(arguments[1]+'000'));
			}else if(arguments[1] < 0){
				t = Math.abs(arguments[1]);
				var d = parseInt(t / 60 / 60 / 24);
				var H = parseInt(t / 60 / 60 % 24);
				var i = parseInt(t / 60 % 60);
				var s = parseInt(t % 60);
				str = str.replace(/d([^dHis]*)/g,d > 0?d+'$1':'');
				str = str.replace(/H([^dHis]*)/g,H > 0?H+'$1':d>0?'0':'');
				str = str.replace(/i([^dHis]*)/g,i > 0?i+'$1':d>0 || H>0?'0':'');
				str = str.replace(/s([^dHis]*)/g,s > 0?s+'$1':d>0 || H>0 || i>0?'0':'');
				return str;
			}else{
				return '';
			}
		break;
	}
	var Y = t.getFullYear();
	var y = t.getFullYear().toString().substr(2);
	var m = t.getMonth()+1;
	var d = t.getDate();
	var H = t.getHours();
	var i = t.getMinutes();
	var s = t.getSeconds();
	if(m < 10)m = '0'+m;
	if(d < 10)d = '0'+d;
	if(H < 10)H = '0'+H;
	if(i < 10)i = '0'+i;
	if(s < 10)s = '0'+s;
	str = str.replace(/Y/g,Y);
	str = str.replace(/y/g,y);
	str = str.replace(/m/g,m);
	str = str.replace(/d/g,d);
	str = str.replace(/H/g,H);
	str = str.replace(/i/g,i);
	str = str.replace(/s/g,s);
	return str;
}

function F(){
	var args = arguments;
	switch(args.length){
		case 1:
			return new Promise(function(resolve, reject) {
				_lib.fs.readFile(args[0],function(err,data){
					if(err){
						resolve();
					}else{
						resolve(data.toString());
					}
				});
			});
		break;
		case 2:
			return new Promise(function(resolve, reject) {
				var dir = _lib.path.dirname(args[0]);
				var f = function(){
					_lib.fs.writeFile(args[0],args[1],function(err,data){
						if(err){
							reject(err);
						}else{
							resolve(data);
						}
					});
				};
				is_file(dir).then(ok=>{
					if(ok){
						f();
					}else{
						mkdir(dir,f);
					}
				})
			});
		break;
	}
};

is_file = function(){
	var fs = require("fs");
	return function(path){
		return new Promise(function(resolve) {
			fs.exists(path,resolve);
		});
	};
}();

mkdir = function(){
	var fs = require('fs');
	var path = require('path');
	return function(dirname,callback){  
		fs.exists(dirname,function(exists){  
			if (exists) {  
				callback();  
			} else {  
				mkdir(path.dirname(dirname), function () {  
					fs.mkdir(dirname, callback);  
				});
			}
		});
	}
}();